'''
This file holds the CodeTree, basically a wrapper for CodeTreeNode
which provides a simpler interface, and keeps track of the root, etc.
for you. 


Okay, basic structure of CodeTree:

root
	members (both methods and fields)
		attributes (type, etc.)
	methods
		method attribute is set. 

@author kllvql
@date 12/22/11

'''

from ProjectFlags import *

TEST  = False
DEBUG = False

TEST  = TEST  or TEST_ALL
DEBUG = DEBUG or DEBUG_ALL

METHOD 		= 'method'
FIELD		= 'field'
FLAG		= 'flag'
STRUCT		= 'struct'
CLASS		= 'class'
RESERVED	= [METHOD,FIELD,FLAG,STRUCT,CLASS]

ARGS		= 'args'
PROGN		= 'progn'
TYPE 		= 'type'
DEFAULT 	= 'default'
GETTERS		= 'getters'
SETTERS		= 'setters'


class CodeTree:
	
	
	def __init__(self, root, name):
		'''
		Root is the name of the root node of this code tree.
		Name is the name of this CodeTree.
		'''
		self.root = self.CodeTreeNode(root)
		self.root.addChild(self.CodeTreeNode(name))

	def __str__(self):
		return str(self.root)

	def getName(self):
		'''
		Gets the name of this CodeTree.
		'''
		return self.name()
	def name(self):
		'''
		Gets the name of this CodeTree
		'''
		return self.getMain().getName()
	
	def getType(self):
		'''
		Gets the type of this CodeTree (class, struct, etc.)
		'''
		return self.root.getName()
	
	def getMain(self):
		'''
		Gets the main element of this CodeTree. The root element will 
		usually be the type, so the root's first child would be the 
		returned element if it is a class or a struct.
		'''
		return self.root.getChildren()[0]
	
	def members(self):
		'''
		Returns memebers in this CodeTree as CodeTreeNodes
		'''
		return self.getMain().getChildren()
	
	def fields(self):
		'''
		Returns elements in the tree declared Fields as CodeTreeNodes
		'''
		fields = []
		for member in self.members():
			if member.getName() == FIELD:
				fields.append(member.getChildren()[0])
			elif self.getType() == STRUCT and \
				 not (member.getName() in RESERVED):
				fields.append(member) 
		return fields
	
	def methods(self):
		'''
		Returns elements in the tree declared Methods as CodeTreeNodes
		'''
		methods = []
		for member in self.members():
			if member.getName() == METHOD:
				methods.append(member.getChildren()[0])
		return methods
	
	def flags(self):
		'''
		Returns flags set in the tree as strings.
		'''
		flags = []
		for member in self.members():
			if member.getName() == FLAG:
				flags.append(member.getChildren()[0].getName())
		return flags
	
	@staticmethod
	def getNode(name):
		'''
		Creator of nodes for the tree.
		'''
		return CodeTree.CodeTreeNode(name)
		
	def getRoot(self):
		'''
		Returns the root node of this CodeTree
		'''
		return self.root
	
	# This is a change I'm unsure about, moving the Node into an 
	# inner class for the CodeTree itself
	class CodeTreeNode:
	

		def __init__(self, name, parent=None, children=None):
			'''
			So, it turns out using [] as a default value just doesn't
			work. Oh well, None as a marker works almost as well.
			(default values are evaluated when the method is defined, 
			and persist with the method)
			'''
			self.name = name
			
			self.parent = parent
			self.children = children if children != None else []
		
		def __iter__(self):
			'''
			Iterates through the tree in a depth-first prefix manner. 
			'''
			yield self
			for child in self.children:
				for grandchild in child:
					yield grandchild
		
		def __str__(self):
			'''
			This returns a string representation of this node. If it is
			an atom (no children) it is returns just the name. If it is
			not, it is returned as a string representation of a 
			S-Expression.
			'''
			returned = '(' + self.name
			for child in self.children:
				returned += ' ' + str(child)
			return returned + ')' 
			
		def _setChildren(self, newChildren):
			self.children = newChildren
			
		def getChildren(self):
			return self.children
		def getChild(self, name):
			for child in self.children:
				if child.getName() == name:
					return child
			for child in self.child:
				test = child.getChild(name)
				if test != None:
					return test
			return None
		
		def addChild(self, child):
			self.children.append(child)
		def addChildren(self, children):
			for child in children:
				self.addChild(child)
		
		def remove(self, name):
			'''
			Removes a given element from the tree. Root node cannot be
			removed through this method. returns true if child removed
			false otherwise.
			'''
			for child in self.children:
				if child.getName() == name:
					self.children.remove(child)
					return True
			for child in self.children:
				if child.remove(name):
					return True
			return False
		
		def setName(self, name):
			self.name = name
			return self
		def getName(self):
			return self.name			
		
		
		
		def hasAttribute(self, name):
			'''
			Returns true iff node has a attribute of given name.
			'''
			for attribute in self.getChildren():
				if attribute.getName() == name:
					return True
			return False
			
			
		
		def type(self):
			'''
			Special case of .attribute for TYPE
			'''
			return self.attribute(TYPE)
		
		def args(self):
			'''
			Special case of .attribute for ARGS, or a list of arguments
			as CodeTreeNodes.
			'''
			return self.attribute(ARGS)
		
		def body(self):
			'''
			Body is the same as progn, just with a different name.
			'''
			return self.progn()
		
		def progn(self):
			'''
			Special case of .attribute for PROGN, a list of 
			CodeTreeNodes which represent a series of commands.
			'''
			return self.attribute(PROGN)
		
		def attribute(self, name):
			'''
			Returns a list of the value of a certain attribute of member
			given by name. 
			'''
			for attribute in self.getChildren():
				if attribute.getName() == name:
					return attribute.getChildren()
			return []
		
		
def test():
	print "CodeTree.py Testing..."
	
	print "End CodeTree.py testing.\n"

if __name__ == '__main__' or TEST:
	test()
		
